package dvonn.game;

import java.awt.Point;
import java.util.ArrayList;

public class GameField {
	
	private Place[][] gameField;
	private int width;
	private int height;

	public GameField() {
		this.width = 11;
		this.height = 5;
		gameField = new Place[this.width][this.height];
		for (int x=0;x<this.width;x++) {
			for (int y=0;y<this.height;y++) gameField[x][y] = new Place();
		}
		this.setToken( new Point( 0, 3), Token.RESERVED);
		this.setToken( new Point( 0, 4), Token.RESERVED);
		this.setToken( new Point( 1, 4), Token.RESERVED);
		this.setToken( new Point(10, 0), Token.RESERVED);
		this.setToken( new Point(10, 1), Token.RESERVED);
		this.setToken( new Point( 9, 0), Token.RESERVED);
	}
	
	public GameField(GameField g) {
		this.width = g.getWidth();
		this.height = g.getHeight();
		this.gameField = new Place[this.width][this.height];
		for (int x=0;x<this.width;x++) {
			for (int y=0;y<this.height;y++) 
				this.gameField[x][y] = new Place(g.getPlace(new Point(x, y)));
		}
	}

	public boolean setToken(Point p, Token t) {
		if (!atField(p.x,p.y)) return false;
		return gameField[p.x][p.y].setToken(t);
	}
	
	public Token getToken(int x, int y) {
		if (!atField(x,y)) return null;
		return gameField[x][y].getToken();
	}
	
	public boolean isValidTarget(int x1, int y1, int x2, int y2) {
		if (!atField(x2,y2)) return false;
		if (gameField[x2][y2].isEmpty()) return false;
		if (gameField[x2][y2].isReserved()) return false;
		int size = gameField[x1][y1].getSize();
		if ((x1==x2)&&((y1+size==y2))||(y1-size==y2)) return true;
		if ((y1==y2)&&((x1+size==x2)||(x1-size==x2))) return true;
		if ((x1+size==x2)&&(y1+size==y2)) return true;
		if ((x1-size==x2)&&(y1-size==y2)) return true;
		return false;
	}
	
	public void doMove(Point from, Point to) {
		// no need !?
		// if (!gameField[from.x][from.y].isMovable() || gameField[from.x][from.y].isEmpty()) return false;
		// if (!isValidTarget(from.x,from.y,to.x,to.y)) return false;
		gameField[to.x][to.y].add( gameField[from.x][from.y] );
		gameField[from.x][from.y].clear();
		setMovable(from.x-1,from.y);
		setMovable(from.x+1,from.y);
		setMovable(from.x,from.y+1);
		setMovable(from.x,from.y-1);
		setMovable(from.x+1,from.y+1);
		setMovable(from.x-1,from.y-1);
		//dvonnChecker();
	}
	
	public void dvonnChecker() {
		ArrayList<Point> unchecked = new ArrayList<Point>();
		ArrayList<Point> toCheck = new ArrayList<Point>();
		for (int x=0; x<this.width; x++) {
			for (int y=0; y<this.height; y++) {
				Place p = gameField[x][y];
				if (!p.isEmpty()&&!p.isReserved()) {
					if (p.withDvonn()) toCheck.add(new Point(x,y));
					else unchecked.add(new Point(x,y));
				}
			}
		}
		System.out.println("unchecked="+unchecked.size()+" , toCheck="+toCheck.size());
		while (!toCheck.isEmpty()) {
			ArrayList<Point> neighbours = getAllNeighbours(toCheck.get(0));
			toCheck.remove(0);
			for (Point pos : neighbours) {
				if (unchecked.contains(pos)) {
					toCheck.add(pos);
					unchecked.remove(pos);
				}
			}
		}
		System.out.println("unchecked="+unchecked.size()+" , toCheck="+toCheck.size());
		if (!unchecked.isEmpty()) {
			for (Point p:unchecked) {
				System.out.println("remove ("+p.x+","+p.y+") "+gameField[p.x][p.y].getSize()+" token(s)");
				gameField[p.x][p.y].clear();
			}
		}
	}
	
	private void setMovable(int x, int y) {
		if (atField(x,y)) gameField[x][y].setMovable();
	}
	
	public ArrayList<Point> getAllMovable(Token t) {
		ArrayList<Point> liste = new ArrayList<Point>();
		for (int x=0;x<this.width;x++) {
			for (int y=0;y<this.height;y++) {
				if (gameField[x][y].isMovable()&&gameField[x][y].getToken()==t) {
					liste.add(new Point(x,y));
				}
			}
		}
		return liste;
	}
	
	public ArrayList<Point> getAllValidTargets(Point p) {
		ArrayList<Point> liste = new ArrayList<Point>();
		if (!atField(p.x,p.y)) return liste;
		int size = gameField[p.x][p.y].getSize();
		if (size>=this.width) return liste;
		if (isValidTarget(p.x,p.y,p.x+size,p.y)) liste.add(new Point(p.x+size,p.y));
		if (isValidTarget(p.x,p.y,p.x-size,p.y)) liste.add(new Point(p.x-size,p.y));
		if (size<this.height) {
			if (isValidTarget(p.x,p.y,p.x,p.y+size)) liste.add(new Point(p.x,p.y+size));
			if (isValidTarget(p.x,p.y,p.x,p.y-size)) liste.add(new Point(p.x,p.y-size));
			if (isValidTarget(p.x,p.y,p.x+size,p.y+size)) liste.add(new Point(p.x+size,p.y+size));
			if (isValidTarget(p.x,p.y,p.x-size,p.y-size)) liste.add(new Point(p.x-size,p.y-size));
		}
		return liste;
	}
	
	public ArrayList<Point> getAllNeighbours(Point pos) {
		int x = pos.x;
		int y = pos.y;
		ArrayList<Point> liste = new ArrayList<Point>();
		Place p;
		if (atField(x-1,y)) {
			p = gameField[x-1][y];
			if (!p.isEmpty()&&!p.isReserved()) liste.add(new Point(x-1,y));
		}
		if (atField(x+1,y)) {
			p = gameField[x+1][y];
			if (!p.isEmpty()&&!p.isReserved()) liste.add(new Point(x+1,y));
		}
		if (atField(x,y-1)) {
			p = gameField[x][y-1];
			if (!p.isEmpty()&&!p.isReserved()) liste.add(new Point(x,y-1));
		}
		if (atField(x,y+1)) {
			p = gameField[x][y+1];
			if (!p.isEmpty()&&!p.isReserved()) liste.add(new Point(x,y+1));
		}
		if (atField(x-1,y-1)) {
			p = gameField[x-1][y-1];
			if (!p.isEmpty()&&!p.isReserved()) liste.add(new Point(x-1,y-1));
		}
		if (atField(x+1,y+1)) {
			p = gameField[x+1][y+1];
			if (!p.isEmpty()&&!p.isReserved()) liste.add(new Point(x+1,y+1));
		}
		return liste;
	}
	
	public void checkAllMovable() {
		for (int x=0; x<this.width; x++) {
			for (int y=0; y<this.height; y++) {
				if (this.getAllNeighbours(new Point(x,y)).size()<6) this.setMovable(x, y);
			}
		}
	}
	
	private boolean atField(int x, int y) {
		if ((x<0)||(x>=this.width)||(y<0)||(y>=this.height)) return false;
		return true;
	}
	
	public String toString() {
		String output = "";
		for (int y=this.height-1; y>=0; y--) {
			for (int s=5-y;s>0;s--) output = output +"   ";
			for (int x=0; x<this.width; x++) {
				if (gameField[x][y].isReserved()) {
					output = output + "      ";
				} else output = output + gameField[x][y];
			}
			output = output + "\n";
		}
		return output;
	}
	
	public int getWidth() {
		return this.width;
	}
	
	public int getHeight() {
		return this.height;
	}
	
	public Place getPlace(Point pos) {
		return gameField[pos.x][pos.y];
	}
	
	public boolean equals(GameField g) {
		if (this.width!=g.getWidth()) return false;
		if (this.height!=g.getHeight()) return false;
		for (int x=0;x<this.width;x++) {
			for (int y=0;y<this.height;y++) {
				if (!gameField[x][y].equals(g.getPlace(new Point(x, y)))) return false;
			}
		}
		return true;
	}
	
	public int[] getScore(Token[] tokens) {
		int n = tokens.length;
		int[] score = new int[n];
		for (int iter=0;iter<n;iter++) {
			score[iter]=0;
			for (int x=0; x<this.width; x++) {
				for (int y=0; y<this.height; y++) {
					if (this.getToken(x,y)==tokens[iter]) score[iter] += gameField[x][y].getSize();
				}
			}
			
		}
		return score;
	}
}
